/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package treepro;

import java.awt.Color;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import javax.swing.DefaultListModel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.table.JTableHeader;

/**
 *
 * @author Ivan
 */
public class TreePanel extends JPanel {

    //public float zoom = 1.0f;
    public int command = 0;
    public TreeProView parent = null;
    ArrayList<GraphicObject> objects;
    public int lastX = 0, lastY = 0;
    public int state = 1;
    public GraphicObject dragged = null;
    public GraphicObject selected = null;
    public BufferedImage buffer = null;
    private int screenWidth = 1024;
    private int screenHeight = 768;

    public LineObject getOrient(LineObject line) {
        ArrayList<LineObject> e = getLines();

        for (Iterator<LineObject> it = e.iterator(); it.hasNext();) {
            LineObject lineObject = it.next();
            if (lineObject.isMyNode(line.getNo2(), line.getNo1())) {
                return lineObject;
            }
        }
        return null;
    }

    public boolean haveOrient(LineObject line) {
        return getOrient(line) != null;
    }

    //Line2D line = null;
    public ArrayList<NodeObject> getNodes() {
        ArrayList<NodeObject> e = new ArrayList<NodeObject>();
        for (Iterator<GraphicObject> it = objects.iterator(); it.hasNext();) {
            GraphicObject graphicObject = it.next();

            if (graphicObject instanceof NodeObject) {
                e.add((NodeObject) graphicObject);
            }
        }
        return e;
    }

    public ArrayList<LineObject> getLines() {
        ArrayList<LineObject> e = new ArrayList<LineObject>();
        for (Iterator<GraphicObject> it = objects.iterator(); it.hasNext();) {
            GraphicObject graphicObject = it.next();
            if (graphicObject instanceof LineObject) {
                e.add((LineObject) graphicObject);
            }
        }
        return e;
    }

    public LineObject findByNumber(int no1, int no2) {
        ArrayList<LineObject> e = getLines();
        for (int i = 0; i < e.size(); i++) {
            if (e.get(i).isMyNode(no1, no2)) {
                return e.get(i);
            }
        }
        return null;
    }

    /**
     * Проверка и переиндексация number
     */
    public void checkNodeIndexes() {
        ArrayList<NodeObject> e = getNodes();
        Collections.sort(e);
        for (int i=0; i<e.size(); i++) {
            e.get(i).number = i+1;
        }
    }
    
    
    public float[][] getTable() {
        checkNodeIndexes();
        ArrayList<NodeObject> e = getNodes();

        int n = e.size();
        float[][] res = new float[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                res[i][j] = Float.NaN;
            }
        }

        ArrayList<LineObject> l = getLines();
        for (Iterator<LineObject> it = l.iterator(); it.hasNext();) {
            LineObject lineObject = it.next();
            //float pt = (patch)?lineObject.distance:(lineObject.distance/(float)lineObject.speed +(float)lineObject.getNo1().unloadTime+(float)lineObject.getNo2().unloadTime);
            res[lineObject.getNo1().getNumber() - 1][lineObject.getNo2().getNumber() - 1] = lineObject.distance;
        }
        return res;
    }

    public int[] getTableLimits() {
        ArrayList<NodeObject> e = getNodes();
        int n = e.size();
        int[] res = new int[n];
        int i = 0;
        Collections.sort(e);
        for (Iterator<NodeObject> it = e.iterator(); it.hasNext();) {
            NodeObject nodeObject = it.next();
            if (nodeObject.fu_enable)
                res[i++] = nodeObject.fu_value;
            else
                res[i++] = getConnectedNodesCount(nodeObject)-1;
        }
        return res;
    }

    public ArrayList<LineObject> getConnectedLines(NodeObject node) {
        ArrayList<LineObject> l = getLines();
        ArrayList<LineObject> res = new ArrayList<LineObject>();
        for (Iterator<LineObject> it = l.iterator(); it.hasNext();) {
            LineObject lineObject = it.next();
            if (lineObject.isMyNode(node))
                res.add(lineObject);
        }
        return res;
    }
    
    public int getConnectedLinesCount(NodeObject node) {
        return getConnectedLines(node).size();
    }
    
    
    public void addNoExist(ArrayList<NodeObject> l, NodeObject n) {
        boolean ad = true;
        for (Iterator<NodeObject> it = l.iterator(); it.hasNext();) {
            NodeObject node = it.next();
            if (n==node) {
                ad = false;
                break;
            }
                
        }
        
        if (ad) {
            l.add(n);
        }
    }
    
    public ArrayList<NodeObject> getConnectedNodes(NodeObject node) {
        //ArrayList<NodeObject> res = new ArrayList<NodeObject>();
        ArrayList<LineObject> l = getConnectedLines(node);
        ArrayList<NodeObject> res = new ArrayList<NodeObject>();
        //int s = 
        for (Iterator<LineObject> it = l.iterator(); it.hasNext();) {
            LineObject lineObject = it.next();
            if (lineObject.getNo1()==node)
                addNoExist(res, lineObject.getNo2());
            else
                if (lineObject.getNo2()==node)
                    addNoExist(res, lineObject.getNo1());
                    
        }
        
        
        return res;
    }
    
    public int getConnectedNodesCount(NodeObject node) {
        return getConnectedNodes(node).size();
    }
    
    public int getScreenHeight() {
        return screenHeight;
    }

    public int getNodesCount() {
        int cnt = 0;
        for (int i = 0; i < objects.size(); i++) {
            if (objects.get(i) instanceof NodeObject) {
                cnt++;
            }
        }
        return cnt;
    }

    public int getScreenWidth() {
        return screenWidth;
    }

    public void setScreenSize(Dimension d) {
        screenWidth = d.width;
        screenHeight = d.height;
        buffer = new BufferedImage(screenWidth, screenHeight, BufferedImage.TYPE_INT_RGB);
        setPreferredSize(d);
        setSize(d);
        repaint();
    }

    public TreePanel() {
        init();


    }
    public TreeTableModel tableModel = null;
    JTableHeader treeTableHeader = null;

    public void updateTable() {
        updateTable(false);
    }
    public void updateTable(boolean print) {
        checkNodeIndexes();
        String[] columnNames = new String[getNodesCount()];
        ArrayList<NodeObject> e = getNodes();
        Collections.sort(e);
        for (int i = 0; i < e.size(); i++) {
            columnNames[i] = e.get(i).getName();
        }



        float[][] f = getTable();
        String[][] data = new String[e.size()][e.size()];
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[i].length; j++) {
                data[i][j] = "" + f[i][j];
            }
        }
        
        tableModel = new TreeTableModel(data, columnNames);
        parent.nodesTable.setModel(tableModel);
        
        DefaultListModel listModel = new DefaultListModel();
        RowHeaderList rowHeader = new RowHeaderList(listModel, parent.nodesTable);
        rowHeader.setFixedCellWidth(50);
        tableModel.setRowListModel(listModel);
        
        JScrollPane scroll = parent.tableScrollPane;
        scroll.setRowHeaderView(rowHeader);
        scroll.getRowHeader().addChangeListener(new javax.swing.event.ChangeListener() {

            @Override
            public void stateChanged(javax.swing.event.ChangeEvent e) {
                JViewport viewport = (JViewport) e.getSource();
                parent.treeScrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y);
            }
        });
        //scroll.setComponentPopupMenu(new TablePopupMenu());
        parent.nodesTable.setInheritsPopupMenu(true);
        scroll.setBackground(parent.getFrame().getBackground());
   
        if (print) {
            scroll.getViewport().setBackground(Color.WHITE);
            parent.nodesTable.getTableHeader().setBackground(Color.WHITE);
            rowHeader.setBackground(Color.WHITE);
        }
        else {
            scroll.getViewport().setBackground(parent.getFrame().getBackground());
            parent.nodesTable.getTableHeader().setBackground(parent.getFrame().getBackground());
            rowHeader.setBackground(parent.getFrame().getBackground());
        }
        //parent.nodesTable.repaint();
    }

    public void removeLinks() {
        // удаляем все связи
        for (int i = objects.size() - 1; i > -1; i--) {
            if (objects.get(i) instanceof LineObject) {
                objects.remove(i);
            }
        }
        updateTable();
        repaint();

    }

    public void deleteSelected() {

        if (selected instanceof NodeObject) {
            NodeObject n = (NodeObject) selected;
            for (int i = objects.size() - 1; i > -1; i--) {
                if (objects.get(i) instanceof LineObject) {
                    if (((LineObject) objects.get(i)).isMyNode(n)) {
                        objects.remove(i);
                    }

                }
            }
            objects.remove(selected);
            command = 0;
            no1_link = null;
            no2_link = null;

        } else if (selected instanceof LineObject) {
            objects.remove(selected);
        }

        selected = null;
        repaint();
        updateTable();
    }
    
    /**
     * Спрятать пути решений
     */
    public void clearSolutions() {
        for (Iterator<GraphicObject> it = objects.iterator(); it.hasNext();) {
            GraphicObject graphicObject = it.next();
            graphicObject.setSolution(false);
        }
    }

    /**
     * Найти нод по номеру
     * @param num
     * @return 
     */
    public NodeObject findByNumber(int num) {
        ArrayList<NodeObject> e = getNodes();
        for (int i = 0; i < e.size(); i++) {
            if (e.get(i).getNumber() == num) {
                return e.get(i);
            }
        }
        return null;
    }

    private void init() {
        initListeners();
        objects = new ArrayList<GraphicObject>();
        buffer = new BufferedImage(screenWidth, screenHeight, BufferedImage.TYPE_INT_RGB);
        treeTableHeader = parent.nodesTable.getTableHeader();
        treeTableHeader.addMouseListener(new ColumnHeaderListener());

    }

    public TreePanel(TreeProView parent) {

        this.parent = parent;
        init();

    }

    public final void initListeners() {
        addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {

            @Override
            public void mouseDragged(java.awt.event.MouseEvent evt) {
                onMouseDragged(evt);
            }

            @Override
            public void mouseMoved(java.awt.event.MouseEvent evt) {
                onMouseMoved(evt);
            }
        });
        addMouseListener(new java.awt.event.MouseAdapter() {

            @Override
            public void mousePressed(java.awt.event.MouseEvent evt) {
                onMousePressed(evt);
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                onMouseReleased(e);
            }
        });
    }
    String str = "";

    public void onMouseMoved(java.awt.event.MouseEvent evt) {
        /*
        for (int i=0; i<objects.size(); i++)
        if (objects.get(i).pointOnMe(evt.getX(), evt.getY())) {
        objects.get(i).onMouseMoved(objects.get(i), evt);
        str = objects.get(i).getName()+" selected: "+objects.get(i).isSelected();
        repaint();
        return;
        }
         */
    }

    private void onMouseReleased(MouseEvent e) {
        dragged = null;
    }

    public void onMouseDragged(java.awt.event.MouseEvent evt) {
        if (dragged != null) {
            dragged.onMouseDragged(dragged, evt);
        }
        /*  for (int i=0; i<objects.size(); i++)
        if (objects.get(i).pointOnMe(evt.getX(), evt.getY())) {
        objects.get(i).onMouseDragged(objects.get(i), new MouseEvent(evt.getComponent(), evt.getID(), evt.getWhen(), 
        evt.getModifiers(), (int)(evt.getX()), 
        (int)(evt.getY()), evt.getClickCount(),
        evt.isPopupTrigger()));
        objects.get(i).onMouseDragged(objects.get(i), evt);
        return;
        }*/
    }

    public void setSelected(GraphicObject o) {
        for (int i = 0; i < objects.size(); i++) {
            objects.get(i).setSelected(false);
        }
        if (o != null) {
            o.setSelected(true);
            selected = o;
            switch (command) {
                case 2:
                    // хотят добавить связь, выбирают 1й объект
                    if (o instanceof NodeObject) {
                        no1_link = (NodeObject) o;
                        command = 3;
                    } else {
                        command = 0;
                        if (parent != null) {
                            parent.resetCommand();
                        }
                        JOptionPane.showMessageDialog(this, "Необходимо выбрать первую вершину для связи!", "Не выполнено", JOptionPane.WARNING_MESSAGE);
                    }
                    break;
                case 3:
                    // хотят добавить связь, выбирают 2й объект
                    if (o instanceof NodeObject) {
                        command = 0;
                        if (parent != null) {
                            parent.resetCommand();
                        }

                        no2_link = (NodeObject) o;
                        if (no1_link == no2_link) {
                            JOptionPane.showMessageDialog(this, "Нельзя привязывать вершину к самой себе!", "Не выполнено", JOptionPane.WARNING_MESSAGE);
                            return;
                        }
                        // Диалог подачи параметров связи!
                        // ПРОВЕРИТЬ на наличие существующей связи
                        if (checkExists()) {
                            JOptionPane.showMessageDialog(this, "Такая связь уже существует!", "Не выполнено", JOptionPane.WARNING_MESSAGE);
                            return;
                        }

                        createLine(no1_link, no2_link);

                    } else {
                        command = 0;
                        if (parent != null) {
                            parent.resetCommand();
                        }

                        JOptionPane.showMessageDialog(this, "Необходимо выбрать вторую вершину для связи!", "Не выполнено", JOptionPane.WARNING_MESSAGE);
                    }
                    break;

            }

            if (parent != null) {
                parent.actionMap.get("delete_object").setEnabled(true);
                parent.actionMap.get("object_properties").setEnabled(true);
                if (selected instanceof LineObject) {
                    // еще 1 проверка на 2 линии
                    if (isOrient((LineObject) selected)) {
                        // связь существует
                        parent.actionMap.get("link_orient").setEnabled(false);
                        parent.actionMap.get("link_select").setEnabled(true);
                    } else {
                        // связи нет
                        parent.actionMap.get("link_orient").setEnabled(true);
                        parent.actionMap.get("link_select").setEnabled(false);
                    }




                }
            }
        } else {
            if (parent != null) {
                parent.actionMap.get("delete_object").setEnabled(false);
                parent.actionMap.get("object_properties").setEnabled(false);
                parent.actionMap.get("link_orient").setEnabled(false);
                parent.actionMap.get("link_select").setEnabled(false);
            }
        }
    }

    /**
     * Существует ли 2я ориент. связь у этой линии?
     * @param link
     * @return 
     */
    public boolean isOrient(LineObject link) {
        for (int i = 0; i < objects.size(); i++) {
            if (objects.get(i) instanceof LineObject) {
                if (((LineObject) objects.get(i)).isMyNode(link.getNo2(), link.getNo1())) {
                    return true;
                }
            }
        }
        return false;
    }

    public void addOrient() {
        if (!(selected instanceof LineObject)) {
            return;
        }
        LineObject link = (LineObject) selected;
        LinkDialog dialog = new LinkDialog(parent.getFrame(), true);
        if (!dialog.execute(link.getNo2(), link.getNo1()))
            return;
        
        if (dialog.line != null) {
            objects.add(0, dialog.line);
            parent.actionMap.get("link_select").setEnabled(true);
            parent.actionMap.get("link_orient").setEnabled(false);
        }



        updateTable();
        repaint();

    }
    NodeObject no1_link = null;
    NodeObject no2_link = null;

    public void link_select() {
        if (!(selected instanceof LineObject)) {
            return;
        }
        LineObject link = (LineObject) selected;
        setSelected(findLink(link.getNo2(), link.getNo1()));
        if (selected != null) {
            selected.setSelected(true);
            setProperties();
        }



    }

    public LineObject findLink(NodeObject no1, NodeObject no2) {
        for (int i = 0; i < objects.size(); i++) {
            if (objects.get(i) instanceof LineObject) {
                if (((LineObject) objects.get(i)).isMyNode(no1, no2)) {
                    return ((LineObject) objects.get(i));
                }
            }
        }
        return null;
    }

    public void onMousePressed(java.awt.event.MouseEvent evt) {
        switch (command) {
            case 1:
                createNode(evt.getX(), evt.getY());

                break;

            default:

                setSelected(null);
                for (int i = objects.size() - 1; i > -1; i--) {
                    if (objects.get(i).pointOnMe(evt.getX(), evt.getY())) {
                        objects.get(i).onMousePressed(objects.get(i), evt);
                        if ((command == 2) && (command == 3)) {
                            dragged = null;
                        } else {
                            dragged = objects.get(i);
                        }
                        setSelected(objects.get(i));
                        if (evt.getClickCount() == 2) {
                            // свойства
                            setProperties();
                        }
                        return;
                    }

                }
                dragged = null;

        }

    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics dbuf = buffer.getGraphics();
        dbuf.setColor(Color.WHITE);
        dbuf.fillRect(0, 0, screenWidth, screenHeight);
        Graphics2D g2 = (Graphics2D) dbuf;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        for (int i = 0; i < objects.size(); i++) {
            if (objects.get(i) instanceof LineObject) {
                objects.get(i).paint(g2);
            }
        }
        for (int i = 0; i < objects.size(); i++) {
            if (objects.get(i) instanceof NodeObject) {
                objects.get(i).paint(g2);
            }
        }

        g2.drawString(str, 10, 20);
        // getWidth(), getHeight(),
        g.drawImage(buffer, 0, 0, /*getWidth(), getHeight(),*/ this);
    }

    public void setProperties() {
        if (selected instanceof NodeObject) {
            selected = (new NodeParamDialog(parent.getFrame(), true)).execute((NodeObject) selected);
        }
        if (selected instanceof LineObject) {
            LinkDialog dialog = new LinkDialog(parent.getFrame(), true);
            if (! dialog.execute((LineObject) selected))
                return;
            selected = dialog.line;
            LineObject line = (LineObject) selected;
            if (line.orient) {
                LineObject oLine = getOrient(line);
                if (oLine==null) {
                    objects.add(0, new LineObject(line.distance, line.nesimetr, true,
                            line.osob, line.speed, line.getNo2().number, line.getNo1().number,
                            line.getNo2().getName() + "-" + line.getNo1().getName()));
                } else {
                    
                    oLine.orient=true;
                    if (!line.osob) {
                        oLine.distance=line.distance;
                        oLine.speed = line.speed;
                    }
                    oLine.osob=line.osob;
                    
                    oLine.nesimetr=line.nesimetr;
                }
                    

            } else {
                LineObject oLine = getOrient(line);
                if (oLine!=null)
                    objects.remove(oLine);
                repaint();
                //parent.actionMap.get("link_select").setEnabled(true);
                parent.actionMap.get("link_orient").setEnabled(true);
            }

        }
        updateTable();
    }

    private boolean checkExists() {
        for (int i = 0; i < objects.size(); i++) {
            if (objects.get(i) instanceof LineObject) {
                if (((LineObject) objects.get(i)).isMyNode(no1_link, no2_link)) {
                    return true;
                }
            }
        }

        return false;
    }

    public void move_graph(int x, int y) {
        for (Iterator<NodeObject> it = getNodes().iterator(); it.hasNext();) {
            NodeObject no = it.next();
            no.setRect(no.getLeft() + x, no.getTop() + y, no.getRight() + x, no.getBottom() + y);
        }

    }
    public int move_step = 10;

    public void move_up() {
        move_graph(0, -move_step);
    }

    public void move_down() {
        move_graph(0, move_step);
    }

    public void move_left() {
        move_graph(-move_step, 0);
    }

    public void move_right() {
        move_graph(move_step, 0);
    }

    public boolean saveToFile(File file) {
        TreeProApp.fileName = file;
        TreeProApp.main.getFrame().setTitle(TreeProApp.titleDivasoft+file.getName());
        return UtilXML.graphicsObjectsToXML(objects, file);
    }

    public boolean loadFromFile(File file) {
        TreeProApp.main.treePanel.objects.clear();
        TreeProApp.resultPaths.clear();
        TreeProApp.loopPaths.clear();
        TreeProApp.paths.clear();
        
        TreeProApp.main.treePanel.repaint();
        TreeProApp.main.clearResults();
        UtilXML.xmlTographicsObjects(file);
        Collections.sort(objects);
        updateTable();
        repaint();
        TreeProApp.main.getFrame().setTitle(TreeProApp.titleDivasoft+file.getName());
        TreeProApp.fileName = file;
        return true;
    }

    public void createLine(NodeObject no1_link, NodeObject no2_link) {
        
        LinkDialog dialog = new LinkDialog(parent.getFrame(), true);
        if (!dialog.execute(no1_link, no2_link))
            return;
        
        LineObject line = dialog.line;
        if (line != null) {
            objects.add(0, line);
            if (line.orient) {
                if (!haveOrient(line)) {
                    objects.add(0, new LineObject(line.distance, line.nesimetr, true,
                            line.osob, line.speed, line.getNo2().number, line.getNo1().number,
                            line.getNo2().getName() + "-" + line.getNo1().getName()));
                    parent.actionMap.get("link_select").setEnabled(true);
                    parent.actionMap.get("link_orient").setEnabled(false);
                } else {
                    parent.actionMap.get("link_select").setEnabled(false);
                    parent.actionMap.get("link_orient").setEnabled(true);
                    //JOptionPane.showMessageDialog(this, "Связь уже существует, новый объект не добавлен!", "Ошибка", JOptionPane.ERROR_MESSAGE);
                }

            } else {
                parent.actionMap.get("link_select").setEnabled(true);
                parent.actionMap.get("link_orient").setEnabled(false);
            }
        }
        repaint();
        updateTable();
    }

    // Преобразовать все связи в неориентированные
    public void link_allConvert() {
        ArrayList<LineObject> l = getLines();
        for (Iterator<LineObject> it = l.iterator(); it.hasNext();) {
            LineObject line = it.next();
            if (!haveOrient(line)) {
                objects.add(0, new LineObject(line.distance, line.nesimetr, true,
                        line.osob, line.speed, line.getNo2().number, line.getNo1().number,
                        line.getNo2().getName() + "-" + line.getNo1().getName()));
                line.orient = true;
            }

        }
        updateTable();
    }

    public void createNode(int x, int y) {
        // Добавляем node
        NodeObject no = new NodeParamDialog(TreeProApp.main.getFrame(), true).execute(x, y);
        if (no != null) {
            objects.add(no);
            updateTable();
            repaint();
        }
        command = 0;
        if (parent != null) {
            parent.resetCommand();
        }
    }

    boolean[] getSkvoz() {
        ArrayList<NodeObject> e = getNodes();
        int n = e.size();
        boolean[] res = new boolean[n];
        int i = 0;
        Collections.sort(e);
        for (Iterator<NodeObject> it = e.iterator(); it.hasNext();) {
            NodeObject nodeObject = it.next();
            res[i++] = nodeObject.skvoz;
        }
        return res;
    }
}
